#include <iostream>   // for std::cout, std::endl, ...
#include <cstdlib>    // for int std::abs(int), you probably also have it without the include
#include <cmath>      // for T std::abs(T) with T=float,double,long double
#include <limits>     // for std::numeric_limits info on a floating point type
#include <iomanip>    // for output formatting: std::setprecision, std::scientific

// Example for "argument-dependent name lookup" (ADL) or Koenig lookup.

namespace la {
    // The namespace "la" groups together classes *and* free methods.
    // This example would also work if we would put all of this in the
    // default namespace...
    
    // A 2D vector class.
    struct vector2
    {
        double x;
        double y;
    };

    // Now we define a "free method" to take abs of vector2,
    // it should be usable like the normal abs function.
    vector2 abs(const vector2& v)
    {
        using std::abs; // this is the important statement!
        return { abs(v.x), abs(v.y) }; // C++11 initializer for vector2
    }

    // For printing to screen with e.g. std::cout << v;
    std::ostream& operator<<(std::ostream& out, const vector2& v)
    {
        out << "{" << v.x << ", " << v.y << "}";
        return out;
    }

}

// Take the absolute value of x and print it to screen.
template <typename T>
void print_abs(const T& x)
{
    using std::abs; // Try commenting this out to see what happens! Important!
    std::cout << "abs(" << x << ") = " << abs(x) << std::endl;
}

int main()
{
    // There lives an abs function in the standard namespace,
    // this is an old creature from C where we had abs(int), fabsf(float),
    // fabs(double), fabsl(long double), ...
    std::cout << "abs(-1) = " << abs(-1) << std::endl;
    // prints: 1; as expected
    std::cout << "abs(-1.12345) = " << abs(-1.12345) << std::endl;
    // prints: 1; this is the function "int abs(int i)" which works by type coercion
    std::cout << "std::abs(-1.12345) = " << std::abs(-1.12345) << std::endl; 
    // prints: 1.12345; this is what we want the C++ overloaded method std::abs for double

    // Now we are going to use the function print_abs defined above:
    print_abs(-1.12345);
    // prints: 1.12345; good! using std::abs which is overloaded for different types
    la::vector2 v({-1.12345, 1.12345});
    print_abs(v);
    // prints: {1.12345, 1.12345}; so this uses la::abs not std::abs! This is Koenig lookup.

    // set output precision and scientific notation
    std::cout << std::setprecision(std::numeric_limits<long double>::digits10+1) 
              << std::scientific;
    long double x = 1;
    x += -9e-17; // not representable by double but okay for long double
    double y = x;
    std::cout << exp(2*x*x) << std::endl;
    // prints: 7.3890560989306468542e+00; is this long double exp?
    std::cout << exp(2*y*y) << std::endl;
    // prints: 7.3890560989306468542e+00; this is double exp!
    using std::exp; // The same important thing again!
    std::cout << exp(2*x*x) << std::endl;
    // prints: 7.3890560989306475676e+00; now this is long double exp!
    // so: should do "using std::exp" when going to use exp, even for standard float types!
    
    return 0;
}
